package dv.apkeep;

import java.util.HashMap;

public class Automata implements Cloneable {
    private State start;
    private State currentState;

    public Automata(String regex) {
        String[] elements = regex.split(",");
        State violateState = new State("violate");
        this.start = new StartState("start"); // WARNING: careful "start" is used by user
        State lastState = this.start;
        for (String element : elements) {
            if (element.equals(".*")) {
                lastState.addAction(".", lastState);
            } else {
                State next = new State(element);
                lastState.addAction(element, next);
                if (!lastState.getActions().containsKey(".")) {
                    lastState.addAction(".", violateState);
                }
                lastState = next;
            }
        }
        lastState.setEnd();

        // NOTE: here we let the last state can further be matched, since reachability a->b is part of a->b->c
        lastState.addAction(".", lastState);
        this.currentState = this.start;
    }

    public void init() {
        this.currentState = this.start;
    }

    public State takeAction(String symbol) {
        this.currentState = this.currentState.transfer(symbol);
        return this.currentState;
    }

    public Object clone()throws CloneNotSupportedException{
        return super.clone();
    }

    private class State {
        private String value;
        protected Boolean isStart = false;
        protected Boolean isEnd = false;
        private HashMap<String, State> actions;

        public State(String value) {
            this.value = value;
            this.actions = new HashMap<>();
        }

        @Override
        public String toString() {
            return this.value;
        }

        public String getValue() {
            return value;
        }

        public void addAction(String symbol, State next) {
            this.actions.put(symbol, next);
        }

        public HashMap<String, State> getActions() {
            return actions;
        }

        public void setEnd() {
            this.isEnd = true;
        }

        public boolean isEnd() {
            return this.isEnd;
        }

        public State transfer(String symbol) {
            if (this.actions.containsKey(symbol)) {
                return this.actions.get(symbol);
            }else {
                return this.actions.get(".");
            }
        }
    }

    private class StartState extends State {

        public StartState(String value) {
            super(value);
            this.isStart = true;
        }
    }

    private class EndState extends State {

        public EndState(String value) {
            super(value);
            this.isEnd = true;
        }
    }

    public static void main(String[] args) {
        Automata a = new Automata("a,.*,b");
        System.out.println(a.takeAction("a").isEnd);
        System.out.println(a.takeAction("c").isEnd);
        System.out.println(a.takeAction("d").isEnd);
        System.out.println(a.takeAction("b").isEnd);
        System.out.println(a.takeAction("x").isEnd);
    }
}
